package apibasej.basic;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import apibasej.basic.config.CommonProperties;
import apibasej.basic.config.ConfigManager;
import apibasej.basic.misc.UtilZip;
import apibasej.basic.misc.io.UtilIO;

public class StaticMisc{

	// **************** MISC ******************
	
	

	
	public static String getLineSeparator(){
        return java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator"));
	}
	
	public static boolean isLinux(){
		String os = System.getProperty("os.name");
		//System.out.println("os>"+os);
		return (""+os).toLowerCase().indexOf("linux")>=0;
	}
	public static boolean isWindows(){
		String os = System.getProperty("os.name");
		//System.out.println("os>"+os);
		return (""+os).toLowerCase().indexOf("windows")>=0;
	}
	
	//return new String[]{resp,err}
	public static String[] execCmd(String cmd) throws IOException{
		Process p = Runtime.getRuntime().exec(cmd);
		byte[] r1 = UtilIO.readInputStream(p.getInputStream());
		byte[] err = UtilIO.readInputStream(p.getErrorStream());
		String charset = ConfigManager.getPropDefaultForAll(CommonProperties.CHARSET);
		return new String[]{new String(r1,charset),new String(err,charset)};
	}
	
	public static final String getStackTrace(Throwable exception) {
		if(exception==null) {
			return null;
		}
		try(StringWriter writer = new StringWriter();
			PrintWriter printWriter = new PrintWriter(writer);
			){
			exception.printStackTrace(printWriter);
			return writer.toString();
		}catch (Throwable e) {//não deve ocorrer
			ConfigManager.logDefAll(Level.SEVERE,"Error on getStackTrace from: "+exception,exception);
			ConfigManager.logDefAll(Level.SEVERE,"Error on getStackTrace error: "+e,e);
			return null;
		}
	}
	
	public static String getPathJavaExecutable(){
	    String javaHome = System.getProperty("java.home");
	    javaHome = javaHome.replaceAll("\\\\", "/");
	    if(!javaHome.endsWith("/")){
	    	javaHome += "/";
	    }
	    
	    if(isWindows()){
	    	//se usar java no win irá abrir a console ...
	    	return javaHome+"bin/javaw";
	    }else{
	    	return javaHome+"bin/java";
	    }
	}

	public static byte[] appArrays(byte[] array1, byte[] array2) {
		if (array1 == null) {
			return array2;
		} else if (array2 == null) {
			return array1;
		}
		byte[] joinedArray = new byte[array1.length + array2.length];
		System.arraycopy(array1, 0, joinedArray, 0, array1.length);
		System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
		return joinedArray;
	}

	
	
	
	// **************** IO ******************
	

	
	public static final byte[] readFile(File f) throws IOException{
		if(!f.exists()){
			return null;
		}
		try(FileInputStream fis = new FileInputStream(f)){
			byte[] b = new byte[(int)f.length()];
			fis.read(b);
			return b;
		}
	}
	public static final void writeBytesInFile(byte[] b, File f) throws IOException{
		try(FileOutputStream fos = new FileOutputStream(f)){
			fos.write(b);
			fos.flush();//o close já faz flush()?
		}
	}
	
	// **************** SEC *******************
	
	// retorna 40 caracteres
	public static String getSHA1HexLowerCase(byte[] bytes) throws NoSuchAlgorithmException{
		byte[] hash = getHash("SHA-1",bytes);
		return encodeHexLowerCase(hash);
	}
	public static byte[] getHash(String algorithm , byte[] bytes) throws NoSuchAlgorithmException{
		return MessageDigest.getInstance(algorithm).digest(bytes);
	}
	public static String encodeHexLowerCase(byte[] bytes){
		return encodeHex(bytes).toLowerCase();
	}
	public static String encodeHexUpperCase(byte[] bytes){
		return encodeHex(bytes).toUpperCase();
	}
	public static String encodeHex(byte[] bytes){
		return encodeHexCommonsCodec(bytes); // melhor desempenho e mais confiável;
	}
    private static String encodeHexCommonsCodec(byte[] data) {
        char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        int l = data.length;
       char[] out = new char[l << 1];
       // two characters form the hex value.
       for (int i = 0, j = 0; i < l; i++) {
           out[j++] = DIGITS[(0xF0 & data[i]) >>> 4 ];
           out[j++] = DIGITS[ 0x0F & data[i] ];
       }
       return String.valueOf(out);
    }
    
	/*
	public static byte[] encryptDefault(String key, byte[] toEnc, boolean zipBeforeEnc) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, IOException{
		//usa MD5 porque fica com 128 bits
		// para usar chaves maiores que 128 bits (16 bytes) deve mudar os jars e confs no \jre\lib\security
		return encrypt(UtilDigest.getMD5(key.getBytes(CHARSET)), toEnc, zipBeforeEnc);
	}
	
	public static byte[] decryptDefault(String key, byte[] encrypted, boolean encryptedIsZipped) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, IOException{
		return decrypt(UtilDigest.getMD5(key.getBytes(CHARSET)), encrypted, encryptedIsZipped);
	}*/
	
	// para usar chaves maiores que 128 bits (16 bytes) deve mudar os jars e confs no \jre\lib\security
	public static byte[] encrypt(byte[] key, byte[] toEnc, boolean zipBeforeEnc) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException{
		SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
	    Cipher cipher = Cipher.getInstance("AES");
	    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
	    if(zipBeforeEnc){
	    	toEnc = UtilZip.zipSingle(toEnc, "f");
	    }
	    byte[] encrypted = cipher.doFinal(toEnc);
	    return encrypted;
	}
	public static byte[] decrypt(byte[] key, byte[] encrypted, boolean encryptedIsZipped) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException{
       SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
       Cipher cipher = Cipher.getInstance("AES");
       //cipher.init(Cipher.ENCRYPT_MODE, skeySpec);//????
       cipher.init(Cipher.DECRYPT_MODE, skeySpec);
       byte[] original = cipher.doFinal(encrypted);
       if(encryptedIsZipped){
    	   original = UtilZip.unzipSingle(original);
       }
       return original;
	}
	
	// **************** ZIP *******************
	
	public static void main(String[] args) throws IOException {
		//byte[] zipped = zipSingle("teste string".getBytes(), "teste.txt");
		//new UtilFile().writeBytesInFile(zipped, "D:/teste1.zip");
		//System.out.println( new String(unzipSingle(zipped)) );
		/*
		LinkedHashMap<String,byte[]> entrys = new LinkedHashMap<>();
		entrys.put("aaa.txt", "AAAA".getBytes());
		entrys.put("bbb.txt", "BBBB".getBytes());
		entrys.put("aaa2.txt", "AAAA".getBytes());
		entrys.put("bbb2.txt", "BBBB".getBytes());
		byte[] zip = zipSeveral(entrys);
		writeBytesInFile(zip, new File("D:/teste11.zip") );
		
		for(Entry<String,byte[]> e : unzipSeveral(zip).entrySet()){
			System.out.println(e.getKey()+" - "+new String(e.getValue()));
		}
		*/
		System.out.println(""+System.currentTimeMillis()+"_"+System.nanoTime()+"_"+Math.random());
		System.out.println(""+System.currentTimeMillis()+"_"+System.nanoTime()+"_"+Math.random());
		System.out.println(""+System.currentTimeMillis()+"_"+System.nanoTime()+"_"+Math.random());
		
	}
	

	
}
